//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/image.h>
#include <buola/image/cfontconfig.h>

#include <buola/image/cfont.h>
#include <buola/image/font/cfontface.h>

#include <fontconfig/fontconfig.h>
#include <fontconfig/fcfreetype.h>

#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H

#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)

namespace buola { namespace img {

static FT_Library sFTLibrary;

/* The load flags passed to FT_Load_Glyph control aspects like hinting and
* antialiasing. Here we compute them from the fields of a FcPattern.
*/
static void get_ft_options_from_pattern(FcPattern *pPattern,CFTOptions &pOpt)
{
    FcBool lAntialias,lVerticalLayout,lHinting,lAutoHint,lBitmap,lEmbolden;
    int lRGBA;
    int lHintStyle;

    pOpt.mAntialias=ANTIALIAS_DEFAULT;
    pOpt.mSubpixel=SUBPIXEL_DEFAULT;
    pOpt.mHintStyle=HINTSTYLE_DEFAULT;
    pOpt.mHintMetrics=HINTMETRICS_DEFAULT;
    pOpt.mLoadFlags=FT_LOAD_DEFAULT;
    pOpt.mExtraFlags=FTEXTRA_NONE;

    /* Check whether to force use of embedded bitmaps */
    if (FcPatternGetBool(pPattern,FC_EMBEDDED_BITMAP,0,&lBitmap)!=FcResultMatch)
        lBitmap=FcFalse;

    /* disable antialiasing if requested */
    if (FcPatternGetBool(pPattern,FC_ANTIALIAS,0,&lAntialias)!=FcResultMatch)
        lAntialias=FcTrue;

    if (lAntialias)
    {
        /* disable hinting if requested */
        if (FcPatternGetBool(pPattern,FC_HINTING,0,&lHinting)!=FcResultMatch)
            lHinting = FcTrue;

        if (FcPatternGetInteger(pPattern,FC_RGBA,0,&lRGBA)!=FcResultMatch)
            lRGBA=FC_RGBA_UNKNOWN;

        switch(lRGBA)
        {
        case FC_RGBA_RGB:
            pOpt.mSubpixel=SUBPIXEL_RGB;
            break;
        case FC_RGBA_BGR:
            pOpt.mSubpixel=SUBPIXEL_BGR;
            break;
        case FC_RGBA_VRGB:
            pOpt.mSubpixel=SUBPIXEL_VRGB;
            break;
        case FC_RGBA_VBGR:
            pOpt.mSubpixel=SUBPIXEL_VBGR;
            break;
        case FC_RGBA_UNKNOWN:
        case FC_RGBA_NONE:
        default:
            pOpt.mSubpixel=SUBPIXEL_DEFAULT;
            break;
        }

        if(pOpt.mSubpixel!=SUBPIXEL_DEFAULT)
        {
            pOpt.mAntialias=ANTIALIAS_SUBPIXEL;
        }

        if (FcPatternGetInteger(pPattern,FC_HINT_STYLE,0,&lHintStyle)!=FcResultMatch)
            lHintStyle = FC_HINT_FULL;

        if (!lHinting)
            lHintStyle = FC_HINT_NONE;

        switch(lHintStyle)
        {
        case FC_HINT_NONE:
            pOpt.mHintStyle=HINTSTYLE_NONE;
            break;
        case FC_HINT_SLIGHT:
            pOpt.mHintStyle=HINTSTYLE_SLIGHT;
            break;
        case FC_HINT_MEDIUM:
        default:
            pOpt.mHintStyle=HINTSTYLE_MEDIUM;
            break;
        case FC_HINT_FULL:
            pOpt.mHintStyle=HINTSTYLE_FULL;
            break;
        }

        /* Force embedded bitmaps off if no hinting requested */
        if (pOpt.mHintStyle==HINTSTYLE_NONE)
            lBitmap=FcFalse;

        if (!lBitmap)
            pOpt.mLoadFlags|=FT_LOAD_NO_BITMAP;
    }
    else
    {
        pOpt.mAntialias=ANTIALIAS_NONE;
    }

    /* force autohinting if requested */
    if (FcPatternGetBool(pPattern,FC_AUTOHINT,0,&lAutoHint) != FcResultMatch)
        lAutoHint=FcFalse;

    if (lAutoHint)
        pOpt.mLoadFlags|=FT_LOAD_FORCE_AUTOHINT;

    if (FcPatternGetBool(pPattern,FC_VERTICAL_LAYOUT,0,&lVerticalLayout)!=FcResultMatch)
        lVerticalLayout=FcFalse;

    if (lVerticalLayout)
        pOpt.mLoadFlags|=FT_LOAD_VERTICAL_LAYOUT;

    if (FcPatternGetBool(pPattern,FC_EMBOLDEN,0,&lEmbolden)!=FcResultMatch)
        lEmbolden=FcFalse;

    if (lEmbolden)
        pOpt.mExtraFlags|=FTEXTRA_EMBOLDEN;
}

/**
 * cairo_ft_font_face_create_for_pattern:
 * @pattern: A fontconfig pattern.  Cairo makes a copy of the pattern
 * if it needs to.  You are free to modify or free @pattern after this call.
 *
 * Creates a new font face for the FreeType font backend based on a
 * fontconfig pattern. This font can then be used with
 * cairo_set_font_face() or cairo_scaled_font_create(). The
 * #cairo_scaled_font_t returned from cairo_scaled_font_create() is
 * also for the FreeType backend and can be used with functions such
 * as cairo_ft_scaled_font_lock_face().
 *
 * Font rendering options are represented both here and when you
 * call cairo_scaled_font_create(). Font options that have a representation
 * in a #FcPattern must be passed in here; to modify #FcPattern
 * appropriately to reflect the options in a #cairo_font_options_t, call
 * cairo_ft_font_options_substitute().
 *
 * The pattern's FC_FT_FACE element is inspected first and if that is set,
 * that will be the FreeType font face associated with the returned cairo
 * font face.  Otherwise the FC_FILE element is checked.  If it's set,
 * that and the value of the FC_INDEX element (defaults to zero) of @pattern
 * are used to load a font face from file.
 *
 * If both steps from the previous paragraph fails, @pattern will be passed
 * to FcConfigSubstitute, FcDefaultSubstitute, and finally FcFontMatch,
 * and the resulting font pattern is used.
 *
 * If the FC_FT_FACE element of @pattern is set, the user is responsible
 * for making sure that the referenced FT_Face remains valid for the life
 * time of the returned #cairo_font_face_t.  See
 * cairo_ft_font_face_create_for_ft_face() for an exmaple of how to couple
 * the life time of the FT_Face to that of the cairo font-face.
 *
 * Return value: a newly created #cairo_font_face_t. Free with
 *  cairo_font_face_destroy() when you are done using it.
 **/

//this version just creates it from the pattern, which means that it will actually be created later, when
//we create scaled fonts or something, so we store the pattern
CFontFace::CFontFace(FcPattern *pPattern,const CFTOptions &pOptions)
{
    mPattern=FcPatternDuplicate(pPattern);

    mFTFace=nullptr;

    mFTOptions=pOptions;
}

CFontFace::CFontFace(FT_Face pFace,const CFTOptions &pOptions)
{
    mPattern=nullptr;

    //TODO:here we should look if the unscaled font already exists in some map, and create it only if it doesn't
    mFTFace=new SFTFace;
    mFTFace->mFace=pFace;
    mFTFace->mHaveScale=false;
    mFTFace->mHaveShape=false;

    mFTOptions=pOptions;
}


CFontFace::CFontFace(const std::string &pFileName,int pID,const CFTOptions &pOptions)
{
    mPattern=nullptr;

    //TODO:here we should look if the unscaled font already exists in some map, and create it only if it doesn't
    //also, we should only store the filename, and only open the face when it is needed, keeping a maximum number
    //of open faces
    mFTFace=new SFTFace;

    if(FT_New_Face(sFTLibrary,pFileName.c_str(),pID,&mFTFace->mFace)!=FT_Err_Ok)
    {
        throw XNotFound("can't open face from file");
    }

    mFTFace->mHaveScale=false;
    mFTFace->mHaveShape=false;

    mFTOptions=pOptions;
}


int CFontFace::GetNumGlyphs()
{
    return mFTFace->mFace->num_glyphs;
}

bool CFontFace::GetFontTable(uint32_t pTag,void *pDst,unsigned int &pLen)
{
    FT_ULong lLen=pLen;
    FT_Error lRet=FT_Load_Sfnt_Table(mFTFace->mFace,pTag,0,(FT_Byte*)pDst,&lLen);
    pLen=lLen;

    return lRet==0;
}

TGlyphIndex CFontFace::GetGlyph(wchar_t pC)
{
    return FT_Get_Char_Index(mFTFace->mFace,pC);
}

int CFontFace::GetUnitsPerEM()
{
    return mFTFace->mFace->units_per_EM;
}

void compute_basis_scale_factors(const mat::CMat23d &pM,double &pBScale,double &pNScale,bool pXBasis=true)
{
    double lDet=std::abs(determinant(pM));

    if(lDet==0)
    {
        pBScale=0;
        pNScale=0;
    }
    else
    {
        double lMajor=norm(transform_vector(pM,CPoint_d(pXBasis?1.0:0.0,pXBasis?0.0:1.0)));
        double lMinor=lDet/lMajor;

        if(pXBasis)
        {
            pBScale=lMajor;
            pNScale=lMinor;
        }
        else
        {
            pBScale=lMinor;
            pNScale=lMajor;
        }
    }

}

void compute_transform (const mat::CMat23d &pM,CFTTransform &pT)
{
    double lXScale,lYScale;

    /* The font matrix has x and y "scale" components which we extract and
     * use as character scale values. These influence the way freetype
     * chooses hints, as well as selecting different bitmaps in
     * hand-rendered fonts. We also copy the normalized matrix to
     * freetype's transformation.
     */

    compute_basis_scale_factors(pM,lXScale,lYScale);

    /* FreeType docs say this about x_scale and y_scale:
     * "A character width or height smaller than 1pt is set to 1pt;"
     * So, we cap them from below at 1.0 and let the FT transform
     * take care of sub-1.0 scaling. */
    pT.mXScale=std::max(lXScale,1.0);
    pT.mYScale=std::max(lYScale,1.0);

    pT.mShape=pM*mat::scaling_2d({1.0/pT.mXScale,1.0/pT.mYScale});
}

/* Temporarily scales an unscaled font to the give scale. We catch
 * scaling to the same size, since changing a FT_Face is expensive.
 */

void CFontFace::SetScale(const mat::C2DTransform &pScale)
{
    //TODO:check if we already scaled to the same scale
    CFTTransform lTransform;
    compute_transform(pScale,lTransform);

    FT_Matrix lMat;

    lMat.xx = DOUBLE_TO_16_16(lTransform.mShape(0,0));
    lMat.xy = DOUBLE_TO_16_16(lTransform.mShape(0,1));
    lMat.yx = DOUBLE_TO_16_16(lTransform.mShape(1,0));
    lMat.yy = DOUBLE_TO_16_16(lTransform.mShape(1,1));

    mFTFace->mHaveShape=(lMat.xx != 0x10000 ||
                lMat.yx != 0x00000 ||
                lMat.xy != 0x00000 ||
                lMat.yy != 0x10000);

    mFTFace->mCurrentFTShape=lMat;
    mFTFace->mCurrentTransform=lTransform;

    FT_Set_Transform(mFTFace->mFace, &lMat, nullptr);

    if(mFTFace->mFace->face_flags & FT_FACE_FLAG_SCALABLE)
    {
        FT_Set_Char_Size(mFTFace->mFace,lTransform.mXScale*64.0+0.5,lTransform.mYScale*64.0+0.5,0,0);
    }
    else
    {
        double lMinDist = INFINITY;
        int i;
        int lBest=0;

        for (i = 0; i < mFTFace->mFace->num_fixed_sizes;i++)
        {
            double lSize = mFTFace->mFace->available_sizes[i].y_ppem / 64.;
            double lDist = std::abs(lSize-lTransform.mYScale);

            if (lDist<lMinDist)
            {
                lMinDist=lDist;
                lBest=i;
            }
        }

        FT_Set_Char_Size(mFTFace->mFace,mFTFace->mFace->available_sizes[lBest].x_ppem,
                         mFTFace->mFace->available_sizes[lBest].y_ppem,0, 0);

        FT_Set_Pixel_Sizes (mFTFace->mFace,mFTFace->mFace->available_sizes[lBest].width,
                            mFTFace->mFace->available_sizes[lBest].height);
    }
}

CFontFace *CFontFace::GetForPattern(FcPattern *pPattern)
{
    if(!sFTLibrary)
    {
        if(FT_Init_FreeType(&sFTLibrary))
            throw XInternal("can't initialize freetype library");
    }

    CFTOptions lOptions;
    get_ft_options_from_pattern(pPattern,lOptions);

    FT_Face lFace=nullptr;

    if(FcPatternGetFTFace(pPattern,FC_FT_FACE,0,&lFace)==FcResultMatch)
    {
        //TODO:now we should check if some font face with the same unscaled font, load flags, extra flags and options exist
        //and return it if it exists
        //it seems that a font face is nothing more than the combination of the unscaled font and those flags

        //if there is no match, create one:
        return new CFontFace(lFace,lOptions);
    }

    FcChar8 *lFileName;
    if(FcPatternGetString(pPattern,FC_FILE,0,&lFileName)==FcResultMatch)
    {
        int lID=0;
        FcPatternGetInteger(pPattern, FC_INDEX, 0, &lID);

        return new CFontFace((char*)lFileName,lID,lOptions);
    }

    //The pattern contains neither a face nor a filename, resolve it later.

    //this doesn't seem to be an error, though the function returns nullptr for the unscaled font
    return new CFontFace(pPattern,lOptions);
}

/*namespace img*/ } /*namespace buola*/ }























#if 0
/*
 * The simple 2x2 matrix is converted into separate scale and shape
 * factors so that hinting works right
 */

/*
 * We create an object that corresponds to a single font on the disk;
 * (identified by a filename/id pair) these are shared between all
 * fonts using that file.  For cairo_ft_font_face_create_for_ft_face(), we
 * just create a one-off version with a permanent face value.
 */

#if CAIRO_HAS_FC_FONT
static cairo_status_t
_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
                   FcPattern                  *pattern);

static cairo_font_face_t *
_cairo_ft_resolve_pattern (FcPattern              *pattern,
               const cairo_matrix_t       *font_matrix,
               const cairo_matrix_t       *ctm,
               const cairo_font_options_t *options);

#endif


static cairo_status_t
_transform_glyph_bitmap (cairo_matrix_t         * shape,
             cairo_image_surface_t ** surface)
{
    cairo_matrix_t original_to_transformed;
    cairo_matrix_t transformed_to_original;
    cairo_image_surface_t *old_image;
    cairo_surface_t *image;
    double x[4], y[4];
    double origin_x, origin_y;
    int orig_width, orig_height;
    int i;
    int x_min, y_min, x_max, y_max;
    int width, height;
    cairo_status_t status;
    cairo_surface_pattern_t pattern;

    /* We want to compute a transform that takes the origin
     * (device_x_offset, device_y_offset) to 0,0, then applies
     * the "shape" portion of the font transform
     */
    original_to_transformed = *shape;

    cairo_surface_get_device_offset (&(*surface)->base, &origin_x, &origin_y);
    orig_width = (*surface)->width;
    orig_height = (*surface)->height;

    cairo_matrix_translate (&original_to_transformed,
                -origin_x, -origin_y);

    /* Find the bounding box of the original bitmap under that
     * transform
     */
    x[0] = 0;          y[0] = 0;
    x[1] = orig_width; y[1] = 0;
    x[2] = orig_width; y[2] = orig_height;
    x[3] = 0;          y[3] = orig_height;

    for (i = 0; i < 4; i++)
      cairo_matrix_transform_point (&original_to_transformed,
                    &x[i], &y[i]);

    x_min = floor (x[0]);   y_min = floor (y[0]);
    x_max =  ceil (x[0]);   y_max =  ceil (y[0]);

    for (i = 1; i < 4; i++) {
    if (x[i] < x_min)
        x_min = floor (x[i]);
    else if (x[i] > x_max)
        x_max = ceil (x[i]);
    if (y[i] < y_min)
        y_min = floor (y[i]);
    else if (y[i] > y_max)
        y_max = ceil (y[i]);
    }

    /* Adjust the transform so that the bounding box starts at 0,0 ...
     * this gives our final transform from original bitmap to transformed
     * bitmap.
     */
    original_to_transformed.x0 -= x_min;
    original_to_transformed.y0 -= y_min;

    /* Create the transformed bitmap */
    width  = x_max - x_min;
    height = y_max - y_min;

    transformed_to_original = original_to_transformed;
    status = cairo_matrix_invert (&transformed_to_original);
    if (unlikely (status))
    return status;

    image = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
    if (unlikely (image->status))
    return image->status;

    /* Draw the original bitmap transformed into the new bitmap
     */
    _cairo_pattern_init_for_surface (&pattern, &(*surface)->base);
    cairo_pattern_set_matrix (&pattern.base, &transformed_to_original);

    status = _cairo_surface_paint (image,
                   CAIRO_OPERATOR_SOURCE,
                   &pattern.base,
                   nullptr);

    _cairo_pattern_fini (&pattern.base);

    if (unlikely (status)) {
    cairo_surface_destroy (image);
    return status;
    }

    /* Now update the cache entry for the new bitmap, recomputing
     * the origin based on the final transform.
     */
    cairo_matrix_transform_point (&original_to_transformed,
                  &origin_x, &origin_y);

    old_image = (*surface);
    (*surface) = (cairo_image_surface_t *)image;
    cairo_surface_destroy (&old_image->base);

    cairo_surface_set_device_offset (&(*surface)->base,
                     _cairo_lround (origin_x),
                     _cairo_lround (origin_y));
    return CAIRO_STATUS_SUCCESS;
}

static const cairo_unscaled_font_backend_t cairo_ft_unscaled_font_backend = {
    _cairo_ft_unscaled_font_destroy,
#if 0
    _cairo_ft_unscaled_font_create_glyph
#endif
};

/* #cairo_ft_scaled_font_t */

static int
_move_to (FT_Vector *to, void *closure)
{
    cairo_path_fixed_t *path = closure;
    cairo_fixed_t x, y;

    x = _cairo_fixed_from_26_6 (to->x);
    y = _cairo_fixed_from_26_6 (to->y);

    if (_cairo_path_fixed_close_path (path) != CAIRO_STATUS_SUCCESS)
    return 1;
    if (_cairo_path_fixed_move_to (path, x, y) != CAIRO_STATUS_SUCCESS)
    return 1;

    return 0;
}

static int
_line_to (FT_Vector *to, void *closure)
{
    cairo_path_fixed_t *path = closure;
    cairo_fixed_t x, y;

    x = _cairo_fixed_from_26_6 (to->x);
    y = _cairo_fixed_from_26_6 (to->y);

    if (_cairo_path_fixed_line_to (path, x, y) != CAIRO_STATUS_SUCCESS)
    return 1;

    return 0;
}

static int
_conic_to (FT_Vector *control, FT_Vector *to, void *closure)
{
    cairo_path_fixed_t *path = closure;

    cairo_fixed_t x0, y0;
    cairo_fixed_t x1, y1;
    cairo_fixed_t x2, y2;
    cairo_fixed_t x3, y3;
    cairo_point_t conic;

    if (! _cairo_path_fixed_get_current_point (path, &x0, &y0))
    return 1;

    conic.x = _cairo_fixed_from_26_6 (control->x);
    conic.y = _cairo_fixed_from_26_6 (control->y);

    x3 = _cairo_fixed_from_26_6 (to->x);
    y3 = _cairo_fixed_from_26_6 (to->y);

    x1 = x0 + 2.0/3.0 * (conic.x - x0);
    y1 = y0 + 2.0/3.0 * (conic.y - y0);

    x2 = x3 + 2.0/3.0 * (conic.x - x3);
    y2 = y3 + 2.0/3.0 * (conic.y - y3);

    if (_cairo_path_fixed_curve_to (path,
                    x1, y1,
                    x2, y2,
                    x3, y3) != CAIRO_STATUS_SUCCESS)
    return 1;

    return 0;
}

static int
_cubic_to (FT_Vector *control1, FT_Vector *control2,
       FT_Vector *to, void *closure)
{
    cairo_path_fixed_t *path = closure;
    cairo_fixed_t x0, y0;
    cairo_fixed_t x1, y1;
    cairo_fixed_t x2, y2;

    x0 = _cairo_fixed_from_26_6 (control1->x);
    y0 = _cairo_fixed_from_26_6 (control1->y);

    x1 = _cairo_fixed_from_26_6 (control2->x);
    y1 = _cairo_fixed_from_26_6 (control2->y);

    x2 = _cairo_fixed_from_26_6 (to->x);
    y2 = _cairo_fixed_from_26_6 (to->y);

    if (_cairo_path_fixed_curve_to (path,
                    x0, y0,
                    x1, y1,
                    x2, y2) != CAIRO_STATUS_SUCCESS)
    return 1;

    return 0;
}

static cairo_status_t
_decompose_glyph_outline (FT_Face         face,
              cairo_font_options_t   *options,
              cairo_path_fixed_t    **pathp)
{
    static const FT_Outline_Funcs outline_funcs = {
    (FT_Outline_MoveToFunc)_move_to,
    (FT_Outline_LineToFunc)_line_to,
    (FT_Outline_ConicToFunc)_conic_to,
    (FT_Outline_CubicToFunc)_cubic_to,
    0, /* shift */
    0, /* delta */
    };
    static const FT_Matrix invert_y = {
    DOUBLE_TO_16_16 (1.0), 0,
    0, DOUBLE_TO_16_16 (-1.0),
    };

    FT_GlyphSlot glyph;
    cairo_path_fixed_t *path;
    cairo_status_t status;

    path = _cairo_path_fixed_create ();
    if (!path)
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);

    glyph = face->glyph;

    /* Font glyphs have an inverted Y axis compared to cairo. */
    FT_Outline_Transform (&glyph->outline, &invert_y);
    if (FT_Outline_Decompose (&glyph->outline, &outline_funcs, path)) {
    _cairo_path_fixed_destroy (path);
    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    status = _cairo_path_fixed_close_path (path);
    if (unlikely (status)) {
    _cairo_path_fixed_destroy (path);
    return status;
    }

    *pathp = path;

    return CAIRO_STATUS_SUCCESS;
}

static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
    CAIRO_FONT_TYPE_FT,
    _cairo_ft_scaled_font_fini,
    _cairo_ft_scaled_glyph_init,
    nullptr,           /* text_to_glyphs */
    _cairo_ft_ucs4_to_index,
    nullptr,           /* show_glyphs */
    _cairo_ft_load_truetype_table,
    _cairo_ft_index_to_ucs4
};

/* #cairo_ft_font_face_t */


static cairo_font_face_t *
_cairo_ft_font_face_get_implementation (void                     *abstract_face,
                    const cairo_matrix_t       *font_matrix,
                    const cairo_matrix_t       *ctm,
                    const cairo_font_options_t *options)
{
    cairo_ft_font_face_t      *font_face = abstract_face;

    /* The handling of font options is different depending on how the
     * font face was created. When the user creates a font face with
     * cairo_ft_font_face_create_for_ft_face(), then the load flags
     * passed in augment the load flags for the options.  But for
     * cairo_ft_font_face_create_for_pattern(), the load flags are
     * derived from a pattern where the user has called
     * cairo_ft_font_options_substitute(), so *just* use those load
     * flags and ignore the options.
     */

#if CAIRO_HAS_FC_FONT
    /* If we have an unresolved pattern, resolve it and create
     * unscaled font.  Otherwise, use the ones stored in font_face.
     */
    if (font_face->pattern) {
    cairo_font_face_t *resolved;

    /* Cache the resolved font whilst the FcConfig remains consistent. */
    resolved = font_face->resolved_font_face;
    if (resolved != nullptr) {
        if (! FcInitBringUptoDate ()) {
        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
        return (cairo_font_face_t *) &_cairo_font_face_nil;
        }

        if (font_face->resolved_config == FcConfigGetCurrent ())
        return cairo_font_face_reference (resolved);

        cairo_font_face_destroy (resolved);
        font_face->resolved_font_face = nullptr;
    }

    resolved = _cairo_ft_resolve_pattern (font_face->pattern,
                          font_matrix,
                          ctm,
                          options);
    if (unlikely (resolved->status))
        return resolved;

    font_face->resolved_font_face = cairo_font_face_reference (resolved);
    font_face->resolved_config = FcConfigGetCurrent ();

    return resolved;
    }
#endif

    return abstract_face;
}

const cairo_font_face_backend_t _cairo_ft_font_face_backend = {
    CAIRO_FONT_TYPE_FT,
#if CAIRO_HAS_FC_FONT
    _cairo_ft_font_face_create_for_toy,
#else
    nullptr,
#endif
    _cairo_ft_font_face_destroy,
    _cairo_ft_font_face_scaled_font_create,
    _cairo_ft_font_face_get_implementation
};

/* implement the platform-specific interface */

#if CAIRO_HAS_FC_FONT
static cairo_status_t
_cairo_ft_font_options_substitute (const cairo_font_options_t *options,
                   FcPattern                  *pattern)
{
    FcValue v;

    if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
    {
    if (FcPatternGet (pattern, FC_ANTIALIAS, 0, &v) == FcResultNoMatch)
    {
        if (! FcPatternAddBool (pattern,
                        FC_ANTIALIAS,
                    options->antialias != CAIRO_ANTIALIAS_NONE))
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);

        if (options->antialias != CAIRO_ANTIALIAS_SUBPIXEL) {
        FcPatternDel (pattern, FC_RGBA);
        if (! FcPatternAddInteger (pattern, FC_RGBA, FC_RGBA_NONE))
            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
        }
    }
    }

    if (options->antialias != CAIRO_ANTIALIAS_DEFAULT)
    {
    if (FcPatternGet (pattern, FC_RGBA, 0, &v) == FcResultNoMatch)
    {
        int rgba;

        if (options->antialias == CAIRO_ANTIALIAS_SUBPIXEL) {
        switch (options->subpixel_order) {
        case CAIRO_SUBPIXEL_ORDER_DEFAULT:
        case CAIRO_SUBPIXEL_ORDER_RGB:
        default:
            rgba = FC_RGBA_RGB;
            break;
        case CAIRO_SUBPIXEL_ORDER_BGR:
            rgba = FC_RGBA_BGR;
            break;
        case CAIRO_SUBPIXEL_ORDER_VRGB:
            rgba = FC_RGBA_VRGB;
            break;
        case CAIRO_SUBPIXEL_ORDER_VBGR:
            rgba = FC_RGBA_VBGR;
            break;
        }
        } else {
        rgba = FC_RGBA_NONE;
        }

        if (! FcPatternAddInteger (pattern, FC_RGBA, rgba))
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    }

    if (options->hint_style != CAIRO_HINT_STYLE_DEFAULT)
    {
    if (FcPatternGet (pattern, FC_HINTING, 0, &v) == FcResultNoMatch)
    {
        if (! FcPatternAddBool (pattern,
                        FC_HINTING,
                    options->hint_style != CAIRO_HINT_STYLE_NONE))
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

#ifdef FC_HINT_STYLE
    if (FcPatternGet (pattern, FC_HINT_STYLE, 0, &v) == FcResultNoMatch)
    {
        int hint_style;

        switch (options->hint_style) {
        case CAIRO_HINT_STYLE_NONE:
        hint_style = FC_HINT_NONE;
        break;
        case CAIRO_HINT_STYLE_SLIGHT:
        hint_style = FC_HINT_SLIGHT;
        break;
        case CAIRO_HINT_STYLE_MEDIUM:
        hint_style = FC_HINT_MEDIUM;
        break;
        case CAIRO_HINT_STYLE_FULL:
        case CAIRO_HINT_STYLE_DEFAULT:
        default:
        hint_style = FC_HINT_FULL;
        break;
        }

        if (! FcPatternAddInteger (pattern, FC_HINT_STYLE, hint_style))
        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
#endif
    }

    return CAIRO_STATUS_SUCCESS;
}

/**
 * cairo_ft_font_options_substitute:
 * @options: a #cairo_font_options_t object
 * @pattern: an existing #FcPattern
 *
 * Add options to a #FcPattern based on a #cairo_font_options_t font
 * options object. Options that are already in the pattern, are not overridden,
 * so you should call this function after calling FcConfigSubstitute() (the
 * user's settings should override options based on the surface type), but
 * before calling FcDefaultSubstitute().
 **/
void
cairo_ft_font_options_substitute (const cairo_font_options_t *options,
                  FcPattern                  *pattern)
{
    if (cairo_font_options_status ((cairo_font_options_t *) options))
    return;

    _cairo_ft_font_options_substitute (options, pattern);
}

static cairo_font_face_t *
_cairo_ft_resolve_pattern (FcPattern              *pattern,
               const cairo_matrix_t       *font_matrix,
               const cairo_matrix_t       *ctm,
               const cairo_font_options_t *font_options)
{
    cairo_status_t status;

    cairo_matrix_t scale;
    FcPattern *resolved;
    cairo_ft_font_transform_t sf;
    FcResult result;
    cairo_ft_unscaled_font_t *unscaled;
    cairo_ft_options_t ft_options;
    cairo_font_face_t *font_face;

    scale = *ctm;
    scale.x0 = scale.y0 = 0;
    cairo_matrix_multiply (&scale,
                           font_matrix,
                           &scale);

    status = _compute_transform (&sf, &scale);
    if (unlikely (status))
    return (cairo_font_face_t *)&_cairo_font_face_nil;

    pattern = FcPatternDuplicate (pattern);
    if (pattern == nullptr)
    return (cairo_font_face_t *)&_cairo_font_face_nil;

    if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) {
    font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
    goto FREE_PATTERN;
    }

    if (! FcConfigSubstitute (nullptr, pattern, FcMatchPattern)) {
    font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
    goto FREE_PATTERN;
    }

    status = _cairo_ft_font_options_substitute (font_options, pattern);
    if (status) {
    font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
    goto FREE_PATTERN;
    }

    FcDefaultSubstitute (pattern);

    resolved = FcFontMatch (nullptr, pattern, &result);
    if (!resolved) {
    /* We failed to find any font. Substitute twin so that the user can
     * see something (and hopefully recognise that the font is missing)
     * and not just receive a NO_MEMORY error during rendering.
     */
    font_face = _cairo_font_face_twin_create_fallback ();
    goto FREE_PATTERN;
    }

    status = _cairo_ft_unscaled_font_create_for_pattern (resolved, &unscaled);
    if (unlikely (status)) {
    font_face = (cairo_font_face_t *)&_cairo_font_face_nil;
    goto FREE_RESOLVED;
    }

    assert (unscaled != nullptr);

    _get_pattern_ft_options (resolved, &ft_options);
    font_face = _cairo_ft_font_face_create (unscaled, &ft_options);
    _cairo_unscaled_font_destroy (&unscaled->base);

FREE_RESOLVED:
    FcPatternDestroy (resolved);

FREE_PATTERN:
    FcPatternDestroy (pattern);

    return font_face;
}

#endif
#endif